package com.witmore.skyline.web.service.third.tplink;

import com.alibaba.fastjson.JSONObject;
import com.witmore.skyline.web.service.third.constants.ApiTpLinkClientConstant;
import com.witmore.skyline.web.service.third.tplink.dto.TpAkAndSkDTO;
import com.witmore.skyline.web.service.third.tplink.dto.TpResultDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.TreeMap;
import java.util.UUID;

import static com.witmore.skyline.web.service.third.constants.ApiTpLinkClientConstant.*;

/**
 * Description: ApiTPClient
 * Author: 豆子高
 * Date: 2025/3/13 16:44
 */
@Slf4j
public class ApiTPClient {


    public static byte[] hmac256(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance(ALGORITHM);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes(UTF8));
    }

    public static String sha256Hex(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance(SHA_256);
        byte[] d = md.digest(s.getBytes(UTF8));
        return DatatypeConverter.printHexBinary(d).toLowerCase();
    }

    /**
     * 发送 POST 请求的方法
     *
     * @param urlString 请求的完整 URL
     * @param payload   请求体数据（例如 JSON 字符串）
     * @param headers   请求头的键值对
     * @return 服务器响应的内容
     * @throws Exception 如果发生网络错误或其他异常
     */
    public static String sendPost(String urlString, String payload, java.util.Map<String, String> headers) throws Exception {
        URL url = new URL(urlString);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // 设置请求方式为 POST
        connection.setRequestMethod(METHOD_POST);

        // 设置连接和读取超时时间（毫秒）
        connection.setConnectTimeout(30000);
        connection.setReadTimeout(30000);

        // 允许输出，即允许向服务器发送数据
        connection.setDoOutput(true);

        // 设置请求头
        if (headers != null) {
            for (java.util.Map.Entry<String, String> entry : headers.entrySet()) {
                connection.setRequestProperty(entry.getKey(), entry.getValue());
            }
        }

        // 发送请求体
        try (OutputStream os = connection.getOutputStream()) {
            byte[] input = payload.getBytes(StandardCharsets.UTF_8);
            os.write(input, 0, input.length);
        }

        // 获取响应码
        int responseCode = connection.getResponseCode();
        log.info("Response Code: {}", responseCode);

        // 读取响应内容
        StringBuilder response = new StringBuilder();
        try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
        } catch (Exception e) {
            // 如果无法读取 InputStream，则尝试读取错误流
            try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
                String errorLine;
                while ((errorLine = errorReader.readLine()) != null) {
                    response.append(errorLine);
                }
            }
        }

        // 关闭连接
        connection.disconnect();
        log.info("Response:{}", response);
        return response.toString();
    }


    public static TreeMap<String, String> authorizationReady(String path, String payload) throws Exception {

        // ************* 步骤 1：准备签名计算相关参数 *************

        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonce = UUID.randomUUID().toString().replaceAll("-", "");
        // ************* 步骤 2：拼接待签名字符串 *************
        String hashedCanonicalRequest = sha256Hex(payload);
        System.out.println("hashedCanonicalRequest:" + hashedCanonicalRequest);
        String credentialScope = METHOD_POST + " " + path + " " + "tp-link_request";
        String stringToSign = ALGORITHM + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
        System.out.println("stringToSign:" + stringToSign);

        // ************* 步骤 3：计算签名 *************
        byte[] secretDate = hmac256(SK.getBytes(UTF8), timestamp);
        byte[] secretService = hmac256(secretDate, path);
        byte[] secretSigning = hmac256(secretService, "tp-link");
        String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
        System.out.println("signature:" + signature);

        // ************* 步骤 4：拼接 Authorization *************
        String authorization = "Timestamp=" + timestamp + "," + "Nonce=" + nonce + "," + "AccessKey=" + AK + "," +
                "Signature=" + signature + "," + "TerminalId=" + TERMINAL_ID;
        System.out.println("authorization:" + authorization);

        TreeMap<String, String> headers = new TreeMap<>();
        headers.put("X-Authorization", authorization);
        headers.put("Content-Type", CT_JSON);
        headers.put("Host", HOST);
        return headers;
    }

    /**
     * 1.4.1.1生成临时AK/SK
     * {
     *     "result": {
     *         "tempAk": "ZWQzMDdhMjU4MzY1OGNiNA==",
     *         "tempSk": "8d7e6e7dc41448e881a847240cb5515b",
     *         "createTime": 1709108242,
     *         "expireTime": 1709111842
     *     },
     *     "error_code": 0
     * }
     */
    public static TpAkAndSkDTO createTempApplicationApiCredential(Long expireTime) {
        try {
            expireTime = expireTime == null ? 1800 : expireTime;
            // 请求体内容
            String payload = "{\"expireTime\": \"" + expireTime + "\"}";
            TreeMap<String, String> headerTreeMap = ApiTPClient.authorizationReady(ApiTpLinkClientConstant.GET_TEMP_AK_SK_PATH, payload);
            String response = ApiTPClient.sendPost(GET_TEMP_AK_SK_URL, payload, headerTreeMap);
            if (StringUtils.isNotEmpty(response)) {
                TpResultDTO tpResultDTO = JSONObject.parseObject(response, TpResultDTO.class);
                String result = tpResultDTO.getResult();
                if (StringUtils.isNotEmpty(result)) {
                    return JSONObject.parseObject(result, TpAkAndSkDTO.class);
                }
            }
        } catch (Exception e) {
            log.error("生成临时AK/SK失败: 失败原因：{}", e.getMessage());
        }
        return null;
    }

}
